using System;
using System.Collections;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Threading;
using System.IO;


struct Player
{
    public int id;
    public string ContentPath;
    public string backupPath;
}

struct Mediainstance
{
    public int AssetID;
    public int PlayerID;
    public int FilesPresentNN;
    public bool FullyPresentNN;
    public string SourcePath;
    public int FileCount;
    public bool isFrameSequence;
    public int FilesPresentBackupNN;
    public bool FullyPresentBackupNN;
}


namespace Distribution
{       
    public class Distribution
    {
        string m_ConnectionString;
        string m_ErrMessage ="";
        private int m_TransferWaitDuration = 0;
        public int threadSleepCount=0;
        bool m_notconnected = false;
        public delegate void ErrorTrace(string message);
        ErrorTrace m_NewTrace;



        public bool paused;
        private bool closing;

        //Property Setting
        public bool P
        {
            get
            {
                return paused;
            }

        }

        private bool c
        {
            get
            {
                return closing;
            }
            set
            {
                closing = value;
            }
        }

        /// <summary>
        /// Initialize the varible and Get the list of player
        /// Using ThreadPool to fire multi-thread to start distribution
        /// </summary>
        /// <param name="Connection">Connection String</param>
        /// <param name="trace">Error Tracer</param>
        public void Init(string Connection, ErrorTrace trace)
        {
            m_NewTrace = trace;
            m_ConnectionString = Connection;
            this.closing = false;
            this.paused = false;

            DataSet ds = new DataSet();
            string command = "sproc_DTB_Sel_Player";
            //string command = "Select * from Player where ID = 1";
            ds = OpenConnection(command, "PLAYER");
            getTransferWaitDuration();

            foreach (DataRow row in ds.Tables["PLAYER"].Rows)
            {

                Player playdevice = new Player();

                playdevice.id = Convert.ToInt32(row["ID"].ToString());
                playdevice.ContentPath = row["ContentPath"].ToString();
                playdevice.backupPath = row["bPath"].ToString();

                //Threadpool Start
                ThreadPool.QueueUserWorkItem(new WaitCallback(DistributionManagement), playdevice);
            }
            ThreadPool.QueueUserWorkItem(new WaitCallback(BackupDelete));
            
        }


        //Retrieve the Global value "TransferWaitDuration
        public void getTransferWaitDuration()
        {
            string selectSQL = "dbo.sproc_DTB_Sel_TransferWaitTime";
            SqlConnection con = new SqlConnection(m_ConnectionString);
            SqlCommand cmd = new SqlCommand(selectSQL, con);
            SqlDataReader reader = null;

            try
            {
                con.Open();
                reader = cmd.ExecuteReader();
                reader.Read();
                m_TransferWaitDuration = Convert.ToInt32(reader["value"].ToString());
                
            }
            catch (Exception err)
            {
                m_ErrMessage = "Error occur while connect to Global Configration, Default Value been used \\n" + err.Message.ToString();
                m_NewTrace(m_ErrMessage);
            }
            finally
            {
                reader.Close();
                con.Close();
            }

        }


        //Distribute the Media to the player
        private void DistributionManagement(object playeDevice)
        {
            Player device = (Player)playeDevice;
            string command = commandControl(1, device);
            DataSet ds = new DataSet();
            string OrignialPath = device.ContentPath;

            //while (true)
            //{
                //ds = OpenConnection(command, "MEDIAINSTANCE");

                //if (ds.Tables["MEDIAINSTANCE"].Rows != null)
                //{

                //    foreach (DataRow row in ds.Tables["MEDIAINSTANCE"].Rows)
                //    {
                //        if (this.paused == true || m_notconnected == true)
                //            OnPause();

                //        //device.ContentPath = OrignialPath;
                //        Mediainstance instance = new Mediainstance();
                //        instance.SourcePath = row["SourcePath"].ToString();
                //        string path = instance.SourcePath;

                //        //Get the filename or the foldername of Media
                //        //string[] foldernames = path.Split('\\');
                //        //string filename = foldernames[foldernames.Length - 1];
                //        string filename = path;
                //        string FilePath = device.ContentPath + "\\" + filename;
                //        string backFilePath = device.backupPath + "\\" + filename;

                //        try
                //        {
                //            if (Directory.Exists(FilePath))
                //            {
                //                instance.FilesPresentNN = Directory.GetFiles(FilePath).Length;
                //                instance.FileCount = Convert.ToInt32(row["FileCount"].ToString());

                //                if (instance.FilesPresentNN >= instance.FileCount)
                //                    instance.FullyPresentNN = true;
                //                else instance.FullyPresentNN = false;
                //            }
                //            else
                //            {
                //                if (File.Exists(FilePath))
                //                    instance.FullyPresentNN = true;
                //                else instance.FullyPresentNN = false;
                //            }

                //            if (Directory.Exists(backFilePath))
                //            {
                //                instance.FilesPresentBackupNN = Directory.GetFiles(backFilePath).Length;

                //                if (instance.FilesPresentBackupNN >= instance.FileCount)
                //                    instance.FullyPresentBackupNN = true;
                //                else instance.FullyPresentBackupNN = false;
                //            }
                //            else
                //            {
                //                if (File.Exists(backFilePath))
                //                    instance.FullyPresentBackupNN = true;
                //                else instance.FullyPresentBackupNN = false;
                //            }
                //        }
                //        catch (Exception err)
                //        {
                //            m_ErrMessage += err.Message;
                //            m_NewTrace(m_ErrMessage);
                //        }

                //        instance.AssetID = Convert.ToInt32(row["AssetID"].ToString());
                //        instance.PlayerID = device.id;

                //        //Update the FullyPresentNN to Database
                //        UpdateFullyPresentNN(instance.FullyPresentNN, instance.FullyPresentBackupNN, instance.AssetID, instance.PlayerID, instance.FileCount);

                //    }
                    //Copy the Missing File
                    CopyMissingFile(playeDevice);
                    //Delet the old file
                    deleteOldFile(playeDevice);

                    int counter = 0;

                    while (counter < 60)
                    {
                        if (this.paused == true || m_notconnected == true)
                            OnPause();

                        Thread.Sleep(1000);
                        counter++;
                    }

                //}
                //else
                //{
                //    int counter = 0;

                //    while (counter < 60)
                //    {
                //        if (this.paused == true || m_notconnected == true)
                //            OnPause();

                //        Thread.Sleep(1000);
                //        counter++;
                //    }
                //}
            ThreadPool.QueueUserWorkItem(new WaitCallback(DistributionManagement), playeDevice);

            //}

        }

        //Establish the connection with given command and return dataset with given table name
        public DataSet OpenConnection(string command, string tableName)
        {
            SqlConnection myConnection;
            SqlCommand myCommand;
            SqlDataAdapter ad_data;

            myConnection = new SqlConnection(m_ConnectionString);
            myCommand = new SqlCommand();
            myCommand.Connection = myConnection;
            myCommand.CommandText = command;
            DataSet ds = new DataSet();

            try
            {
                myConnection.Open();
                ad_data = new SqlDataAdapter(myCommand);
                ad_data.Fill(ds, tableName);

            }
            catch (SqlException err)
            {
                if (err.Number != 1205)
                {
                    m_ErrMessage = "Device Not Connected \\n" + err.Message.ToString();
                    m_NewTrace(m_ErrMessage);
                    m_notconnected = true;
                }
            }
            finally
            {
                myConnection.Close();
            }

            return ds;

        }
        
        //Copy Files to players
        private void CopyMissingFile(object playeDevice)
        {
            Player device = (Player)playeDevice;
            DataSet ds = new DataSet();
            string command = commandControl(1, device);
            ds = OpenConnection(command, "MEDIAINSTANCE");

            foreach (DataRow row in ds.Tables["MEDIAINSTANCE"].Rows)
            {
                Mediainstance instance2 = new Mediainstance();

                instance2.FullyPresentNN = Convert.ToBoolean(row["FullyPresentNN"]);
                instance2.FullyPresentBackupNN = Convert.ToBoolean(row["FullyPresentBackupNN"]);
                instance2.isFrameSequence = Convert.ToBoolean(row["isFrameSequence"]);
                instance2.SourcePath = row["SourcePath"].ToString();
                instance2.AssetID = Convert.ToInt32(row["AssetID"].ToString());
                instance2.PlayerID = device.id;

                if (this.paused == true || m_notconnected == true)
                    OnPause();

                if (instance2.FullyPresentNN != true || instance2.FullyPresentBackupNN != true)
                {
                    //Check if the type of Media
                    if (instance2.isFrameSequence)
                    {
                        //string[] filenames = instance2.SourcePath.Split('\\');
                        //string foldername = filenames[filenames.Length - 1];
                        string foldername = instance2.SourcePath;

                        //Create the Directory if the fold is not exist
                        if (!Directory.Exists(device.ContentPath + "\\" + foldername))
                            Directory.CreateDirectory(device.ContentPath + "\\" + foldername);

                        if (!Directory.Exists(device.backupPath + "\\" + foldername))
                            Directory.CreateDirectory(device.backupPath + "\\" + foldername);

                        try
                        {
                            string[] SourceDirs = Directory.GetFiles("Z:\\Media\\" + foldername);
                            ArrayList differentDir = new ArrayList();
                            ArrayList differentBackDir = new ArrayList();

                            string[] DestinDirs = Directory.GetFiles(device.ContentPath + "\\" + foldername);
                            string[] DestinBackDirs = Directory.GetFiles(device.backupPath + "\\" + foldername);

                            //Get the List of Missing Filename

                            GetDifferentList(SourceDirs, DestinDirs, differentDir);
                            GetDifferentList(SourceDirs, DestinBackDirs, differentBackDir);


                            //Distribut the Missing File to Local Player and Backup Player
                            StartCopyFile(differentDir, "Z:\\Media\\"+ instance2.SourcePath, device.ContentPath + "\\" + foldername, playeDevice);
                            StartCopyFile(differentBackDir, "Z:\\Media\\" + instance2.SourcePath, device.backupPath + "\\" + foldername, playeDevice);
                        }
                        catch (Exception err)
                        {
                            m_ErrMessage += err;
                            m_NewTrace(m_ErrMessage);
                        }
                    }
                    else  //if the file is not sequence
                    {
                        string path = instance2.SourcePath;
                        //string[] filenames = path.Split('\\');
                        //string fname = filenames[filenames.Length - 1];
                        string fname = path;
                        string path2 = device.ContentPath +"\\"+ fname;
                        string path3 = device.backupPath + "\\" +  fname;

                        StartCopyFile(path, path2);
                        StartCopyFile(path, path3);
                    }
                    instance2.FullyPresentNN = true;
                    instance2.FullyPresentBackupNN = true;
                    instance2.FilesPresentNN = Convert.ToInt32(row["FileCount"].ToString());
                    instance2.FilesPresentBackupNN = instance2.FilesPresentNN;
                    
                    Update(instance2.FilesPresentNN, instance2.FullyPresentNN, instance2.FilesPresentBackupNN,instance2.FullyPresentBackupNN, instance2.AssetID, instance2.PlayerID);

                    Thread.Sleep(m_TransferWaitDuration); // Wait for Trnasfer Duration
                }
            }
        }

        private void StartCopyFile(string source, string path)
        {
            if (!File.Exists(path))
            {
                try
                {
                    File.Copy(source, path);
                }
                catch (FileNotFoundException err)
                {
                    m_ErrMessage = "Error in File Path \\n" + err.Message.ToString();
                    m_NewTrace(m_ErrMessage);
                }
            }
        }

        private void StartCopyFile(ArrayList differentDir, string path, string path2, object playeDevice )
        {
            int counter = 0;

            foreach (string files in differentDir)
            {
                if (this.paused == true || m_notconnected == true)
                    OnPause();

                try
                {
                    string tmPath = path + "\\" + files;
                    string tmPath2 = path2 + "\\" + files;
                    File.Copy(tmPath, tmPath2, true);

                    // Copy the file 
                    if (counter < 3)
                    {
                        counter++;
                    }
                    else
                    {
                        counter = 0;
                        Thread.Sleep(100);
                    }
                }

                catch (FileNotFoundException err)
                {
                    m_ErrMessage = "Error in File Path \\n" + err.Message.ToString();
                    m_NewTrace(m_ErrMessage);
                }

            }
        }

        //Get the list of missing file into ArryList
        private void GetDifferentList(string[] SourceDirs, string[]DestinDirs, ArrayList differentDir)
        {
            int miss = 0;

            foreach (string fs in SourceDirs)
            {
                if (this.paused == true || m_notconnected == true)
                    OnPause();

                string[] filenames = fs.Split('\\');
                string fname = filenames[filenames.Length - 1];

                if (DestinDirs.Length == 0)
                    differentDir.Add(fname);
                else
                {
                    foreach (string Dfs in DestinDirs)
                    {
                        string[] filenames2 = Dfs.Split('\\');
                        string fname2 = filenames2[filenames2.Length - 1];

                        if (fname == fname2)
                        {
                            miss = 0;
                            break;
                        }
                        else { miss = 1; }
                    }
                    if (miss == 1)
                        differentDir.Add(fname);
                }
            }
        }

        //Scan the Database to see if any file need to be removed from the all Player
        private void BackupDelete(Object stateInfo)
        {
            string command = "Select ContentPath From Player";
            string command2 = "sproc_DTB_Sel_MediainstanceDelete_Check";

            SqlConnection myConnection;
            SqlCommand myCommand;
            SqlDataAdapter ad_data;

            myConnection = new SqlConnection(m_ConnectionString);
            myCommand = new SqlCommand();
            myCommand.Connection = myConnection;
            myCommand.CommandText = command;
            DataSet ds = new DataSet();

            if (this.paused == true || m_notconnected == true)
                OnPause();

            try
            {
                myConnection.Open();
                //Fill the Dataset with the path of all players
                ad_data = new SqlDataAdapter(myCommand);
                ad_data.Fill(ds, "PLAYERPATH");
                
                //Fill The Dataset with the Media need to be deleted for Player
                myCommand.CommandText = command2;
                ad_data = new SqlDataAdapter(myCommand);
                ad_data.Fill(ds, "DELETEMEDIA");

            }
            catch (Exception err)
            {
                m_ErrMessage = "Device Not Connected \\n" + err.Message.ToString();
                m_NewTrace(m_ErrMessage);
                m_notconnected = true;
            }
            finally
            {
                myConnection.Close();
            }

            if (ds.Tables["DELETEMEDIA"].Rows != null)
            {
                foreach (DataRow row in ds.Tables["DELETEMEDIA"].Rows)
                {
                    if (this.paused == true || m_notconnected == true)
                        OnPause();

                    //string[] fileNames = row["SourcePath"].ToString().Split('\\');
                    //string fileName = fileNames[fileNames.Length - 1];
                    string fileName = row["SourcePath"].ToString();

                    foreach (DataRow row2 in ds.Tables["PLAYERPATH"].Rows)
                    {
                        string fN = row2["ContentPath"].ToString() + "\\" + fileName;
                        
                        if (Directory.Exists(fN))
                            Directory.Delete(fN, true);
                        else if(File.Exists(fN))
                            File.Delete(fN);
                    }

                }
                    
            }

            Thread.Sleep(3600000);
            ThreadPool.QueueUserWorkItem(new WaitCallback(BackupDelete));
        }
       //Delete Old File located on Player
        private void deleteOldFile(object playeDevice)
        {
            Player device = (Player)playeDevice;
            DataSet ds = new DataSet();
            string command = commandControl(2, device);
            ds = OpenConnection(command, "DELETEMEDIAINSTANCE");

            if (ds.Tables["DELETEMEDIAINSTANCE"].Rows != null)
            {
                //For each of file(s) that meet the select requirment, delete the files
                foreach (DataRow row in ds.Tables["DELETEMEDIAINSTANCE"].Rows)
                {
                    if (this.paused == true || m_notconnected == true)
                        OnPause();

                    Mediainstance instance2 = new Mediainstance();

                    instance2.AssetID = Convert.ToInt32(row["AssetID"].ToString());
                    instance2.PlayerID = Convert.ToInt32(row["PlayerID"].ToString());
                    instance2.isFrameSequence = Convert.ToBoolean(row["IsFrameSequence"].ToString());
                    instance2.SourcePath = row["SourcePath"].ToString();

                    string[] foldernames = instance2.SourcePath.Split('\\');
                    string filename = foldernames[foldernames.Length - 1];
                    string FilePath = device.ContentPath + "\\"+ filename;

                    if (this.paused == true || m_notconnected == true)
                        OnPause();
                    //Update(0, false, instance2.AssetID, instance2.PlayerID);

                    if (instance2.isFrameSequence)
                    {
                        if (Directory.Exists(FilePath))
                            Directory.Delete(FilePath, true);
                    }

                    else File.Delete(FilePath);

                    Thread.Sleep(m_TransferWaitDuration);

                }
            }

        }

        //Update the Database
        private void DataBaseUpdate(string command)
        {
            SqlConnection con = new SqlConnection(m_ConnectionString);
            SqlCommand cmd = new SqlCommand(command, con);

            // Try to open the database and execute the update.
            try
            {
                con.Open();
                cmd.ExecuteNonQuery();
            }
            catch (Exception err)
            {
                m_ErrMessage = "Error occured while connect to SQL Server \\n" + err.Message.ToString();
                m_NewTrace(m_ErrMessage);
                m_notconnected = true;
            }

            finally
            {
                con.Close();
            }

        }


        //Update the FullPresentNN
        private void UpdateFullyPresentNN(bool FullyPresentNN, bool FullyPresentBackupNN,  int AssetID, int PlayerID , int FileCount)
        {
            string updateSQL;

            updateSQL = "dbo.sproc_DTB_Up_FullPresentNN @FullyPresentNN ='" + FullyPresentNN + "' , @FullyPresentbackupNN='" +FullyPresentBackupNN + "' ,@AssetID = " + AssetID + " , @PlayerID =" + PlayerID + ", @FileCount=" + FileCount;

            DataBaseUpdate(updateSQL);
        }

        //Update the FilesPresentNN and FullyPresentNN togather
        private void Update(int FilesPresentNN, bool FullyPresentNN, int FilesPresentBackupNN, bool FullyPresentBackupNN, int AssetID, int PlayerID)
        {
            string updateSQL;

            updateSQL = "dbo.sproc_DTB_Up_Mediainstance @Fu;llyPresentNN ='"+FullyPresentNN+"', @FilesPresentNN="+ FilesPresentNN +" , @FullyPresentBackupNN = '"+ FullyPresentBackupNN +"', @FilesPresentBackupNN="+ FilesPresentBackupNN +",  @AssetID=" +AssetID+ " , @PlayerID="+PlayerID;

            DataBaseUpdate(updateSQL);
        }

        //Return the type of needed command
        private string commandControl(int index, Player device)
        {
            string command1, command2;

            command1 = "dbo.sproc_DTB_Sel_Mediainstance @PlayerID ="+device.id;
            command2 = "dbo.sproc_DTB_Sel_MediainstanceToDelete @PlayerID ="+device.id;
            //command3 = "dbo.Up_Mediainstance_Del_Update @PlayerID =" + device.id;
            

            if (index == 1)
                return command1;
            else
                return command2;
            //else
            //    return command3;

        }

        //paused the Thread or Kill the Thread if this.closing = true
        private void OnPause()
        {
            //string command;
            //Console.WriteLine(device.id);
            //command = commandControl(3, device);
            //DataBaseUpdate(command);

            threadSleepCount++;

            while (this.paused)
            {
                if (!this.closing)
                {
                    //DataBaseUpdate(command);
                    Thread.Sleep(1000);
                }
                else
                {
                    Thread.CurrentThread.Abort();
                }
            }

            threadSleepCount = 0;
           // ThreadPool.QueueUserWorkItem(new WaitCallback(DistributionManagement), playeDevice);
        }


        public void Pause()
        {
            this.paused = true;
        }

        public void UnPause()
        {
            this.paused = false;
        }

        public void CloseAll()
        {
            this.paused = true;
            this.closing = true;
            
        }

    }
}
